package org.study.dynamic.ds.strategy;

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.study.dynamic.ds.constant.BusinessException;
import org.study.dynamic.ds.constant.ScreenModelEnum;
import org.study.dynamic.ds.dto.*;
import org.study.dynamic.ds.mapper.SqlParseMapper;
import org.study.dynamic.ds.mapper.TDengineSqlParseMapper;
import org.study.dynamic.ds.vo.HistogramTemplateVo;
import org.study.dynamic.ds.vo.ScreenModelVo;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 作用描述
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年06月09日
 */
@Slf4j
@RequiredArgsConstructor
@Component("tdengine")
public class SqlParseStrategy4TDengine extends SqlParseStrategy {
    private final SqlParseMapper sqlParseMapper;
    private final TDengineSqlParseMapper tdengineSqlParseMapper;

    @Override
    public ScreenModelVo parseSql(ParseDto parseDto) {
        // 校验入参
        this.checkParam(parseDto);

        if (StringUtils.isBlank(parseDto.getSql())) {
            if (CollectionUtils.isEmpty(parseDto.getDimension())) {
                // 可能为null，后面会报错，兼容处理
                parseDto.setDimension(Lists.newArrayList());
            }
            // 重写入参
            Optional<ParseColumnDto> rewriteOptional = parseDto.getDimension().stream()
                    .filter(e -> "year".equalsIgnoreCase(e.getSummaryMethod()) || "month".equalsIgnoreCase(e.getSummaryMethod()) || "day".equalsIgnoreCase(e.getSummaryMethod()) || "hour".equalsIgnoreCase(e.getSummaryMethod()) || "minute".equalsIgnoreCase(e.getSummaryMethod()) || "seconds".equalsIgnoreCase(e.getSummaryMethod()))
                    .findAny();
            if (rewriteOptional.isPresent()) {
                // 使用降采样查询
                this.doReWrite(parseDto);
                parseDto.setExcludeTs(true);
            } else {
                // 统计查询函数
                Set<ParseColumnDto> summaryMethodSet = parseDto.getIndex().stream()
                        .filter(index -> StringUtils.isNotBlank(index.getSummaryMethod()))
                        .collect(Collectors.toSet());
                if (CollectionUtils.isNotEmpty(summaryMethodSet)) {
                    // 存在求和(sum)、平均值(avg)、最大值(max)、最小值(min)、计数(count)时，确保其他列加入到group by子句中
                    Set<ParseColumnDto> newGroupSet = Sets.newHashSet();
                    Set<ParseColumnDto> groupSet = parseDto.getIndex().stream()
                            .filter(index -> StringUtils.isBlank(index.getSummaryMethod()))
                            .collect(Collectors.toSet());
                    newGroupSet.addAll(parseDto.getDimension());
                    newGroupSet.addAll(groupSet);
                    parseDto.setDimension(new ArrayList<>(newGroupSet));
                    // 重写指标
                    List<ParseColumnDto> newIndexList = Lists.newArrayList();
                    List<String> dimensionNameList = parseDto.getDimension().stream()
                            .map(ParseColumnDto::getColumnName)
                            .collect(Collectors.toList());
                    newIndexList.addAll(parseDto.getDimension());
                    List<ParseColumnDto> collect = parseDto.getIndex().stream()
                            .filter(e -> !dimensionNameList.contains(e.getColumnName()))
                            .collect(Collectors.toList());
                    newIndexList.addAll(collect);
                    parseDto.setIndex(newIndexList);
                } else {
                    // 重写指标：没有统计函数的维度，直接放到指标中去
                    List<ParseColumnDto> indexList = Lists.newArrayList();
                    List<ParseColumnDto> oldIndexList = parseDto.getIndex().stream()
                            .filter(index -> StringUtils.isBlank(index.getSummaryMethod()))
                            .collect(Collectors.toList());
                    indexList.addAll(parseDto.getDimension());
                    indexList.addAll(oldIndexList);
                    parseDto.setIndex(indexList);
                    parseDto.setDimension(Lists.newArrayList());
                }
            }
        }

        // 手动设置目标数据源
        DynamicDataSourceContextHolder.push("test_tdengine");
        // 查询结果
        //List<LinkedHashMap<String, Object>> data = sqlParseMapper.selectParseListFromTdengine(parseDto);
        List<LinkedHashMap<String, Object>> data = tdengineSqlParseMapper.selectParseList(parseDto);
        // 清除手动设置的数据源
        DynamicDataSourceContextHolder.clear();

        // 转换结果
        this.convertTimestamp(parseDto, data);

        Object result;
        if (StringUtils.isNotBlank(parseDto.getSql())) {
            result = this.convertSql(parseDto, data);
        } else {
            switch (parseDto.getTypeCode()) {
                case 1:
                    // 地图
                    result = this.convertMap(parseDto, data);
                    break;
                case 2:
                    // 柱状图
                    result = this.convertBarChart(parseDto, data);
                    break;
                case 3:
                    // 折线图
                    result = this.convertLineChart(parseDto, data);
                    break;
                case 4:
                    // 饼图
                    result = this.convertPieChart(parseDto, data);
                    break;
                case 5:
                    // 列表
                    result = this.convertTableList(parseDto, data);
                    break;
                case 6:
                    // 散点图
                    result = this.convertScatter(parseDto, data);
                    break;
                default:
                    result = data;
                    break;
            }
        }

        // 组装出参
        ScreenModelVo screenModelVo = new ScreenModelVo();
        screenModelVo.setTypeCode(parseDto.getTypeCode());
        screenModelVo.setTypeName(ScreenModelEnum.getValue(parseDto.getTypeCode()).getDesc());
        screenModelVo.setData(result);
        return screenModelVo;
    }

    @Override
    protected void checkParam(ParseDto parseDto) {
        // 模板类型编码必传
        if (null == parseDto.getTypeCode()) {
            throw new BusinessException("C1001", "模板类型不能为空");
        }
        // 纯SQL校验，SQL安全性校验
        if (StringUtils.isNotBlank(parseDto.getSql()) && !parseDto.getSql().trim().matches("^\\s*(?i)SELECT\\b.*")) {
            throw new BusinessException("C1002", "SQL语句不合法，请检查SQL语句");
        } else {
            // 表名
            if (StringUtils.isBlank(parseDto.getTableName())) {
                throw new BusinessException("C1003", "请选择一个数据源");
            }
            // 指标
            if (CollectionUtils.isEmpty(parseDto.getIndex())) {
                throw new BusinessException("C1004", "请至少选择一个指标");
            } else {
                Optional<ParseColumnDto> emptyDimensionOptional = parseDto.getIndex().stream()
                        .filter(e -> StringUtils.isBlank(e.getColumnName()) || StringUtils.isBlank(e.getColumnComment()))
                        .findAny();
                if (emptyDimensionOptional.isPresent()) {
                    throw new BusinessException("C1005", "指标字段配置不完整，请检查");
                }
            }
            // 过滤
            if (CollectionUtils.isNotEmpty(parseDto.getFilter())) {
                List<ParseFilterItemDto> filterItemList = Lists.newArrayList();
                parseDto.getFilter().forEach(e -> filterItemList.addAll(e.getFilters()));
                Optional<ParseFilterItemDto> errorParamOptional = filterItemList.stream()
                        .filter(e -> StringUtils.isBlank(e.getColumnName()) || StringUtils.isBlank(e.getFilterSymbol()) || StringUtils.isBlank(e.getFilterValue()))
                        .findAny();
                if (errorParamOptional.isPresent()) {
                    throw new BusinessException("C1006", "过滤字段配置不完整，请检查");
                }
            }
            // 排序规则
            if (CollectionUtils.isNotEmpty(parseDto.getOrderBy())) {
                Optional<ParseOrderDto> errorParamOptional = parseDto.getOrderBy().stream()
                        .filter(e -> StringUtils.isBlank(e.getColumnName()) || StringUtils.isBlank(e.getOrderRule()))
                        .findAny();
                if (errorParamOptional.isPresent()) {
                    throw new BusinessException("C1006", "排序字段配置不完整，请检查");
                }
            }
        }
    }

    private void doReWrite(ParseDto parseDto) {
        //TDengine降采样查询，支持：年(y)、月(n)、日(d)、时(h)、分(m)、秒(s)
        //SELECT _wstart, sum(current) FROM d1001 INTERVAL(1s);
        // 重写维度
        parseDto.getDimension().forEach(parseColumnDto -> {
            switch (parseColumnDto.getSummaryMethod()) {
                case "year":
                    parseColumnDto.setSummaryMethod("");
                    parseDto.setInterval("INTERVAL(1y)");
                    parseDto.setIntervalPattern("yyyy");
                    break;
                case "month":
                    parseColumnDto.setSummaryMethod("");
                    parseDto.setInterval("INTERVAL(1n)");
                    parseDto.setIntervalPattern("yyyy-MM");
                    break;
                case "day":
                    parseColumnDto.setSummaryMethod("");
                    parseDto.setInterval("INTERVAL(1d)");
                    parseDto.setIntervalPattern("yyyy-MM-dd");
                    break;
                case "hour":
                    parseColumnDto.setSummaryMethod("");
                    parseDto.setInterval("INTERVAL(1h)");
                    parseDto.setIntervalPattern("yyyy-MM-dd HH");
                    break;
                case "minute":
                    parseColumnDto.setSummaryMethod("");
                    parseDto.setInterval("INTERVAL(1m)");
                    parseDto.setIntervalPattern("yyyy-MM-dd HH:mm");
                    break;
                case "seconds":
                    parseColumnDto.setSummaryMethod("");
                    parseDto.setInterval("INTERVAL(1s)");
                    parseDto.setIntervalPattern("yyyy-MM-dd HH:mm:ss");
                    break;
                default:
                    break;
            }
        });
        // 重写指标
        List<ParseColumnDto> indexList = Lists.newArrayList(ParseColumnDto.builder().columnName("_wstart").columnComment("时间").build());
        indexList.addAll(parseDto.getIndex());
        parseDto.setIndex(indexList);
        // 降采样查询不能与group by子句同时使用
        parseDto.setDimension(Lists.newArrayList());
    }

    private void convertTimestamp(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        String pattern = "yyyy-MM-dd HH:mm:ss";
        if (StringUtils.isNotBlank(parseDto.getIntervalPattern())) {
            pattern = parseDto.getIntervalPattern();
        }
        for (LinkedHashMap<String, Object> item : data) {
            Object datetime = item.get("时间");
            if (datetime != null && (datetime instanceof Timestamp)) {
                DateFormat format = new SimpleDateFormat(pattern);
                item.put("时间", format.format((Timestamp) datetime));
            }
            for (Map.Entry<String, Object> entry : item.entrySet()) {
                if (parseDto.getExcludeTs() != null && parseDto.getExcludeTs() && "_ts".equals(entry.getKey())) {
                    item.remove(entry.getKey());
                    break;
                }
            }
        }
    }

    private Object convertSql(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        HistogramTemplateVo histogramTemplateVo = new HistogramTemplateVo();
        histogramTemplateVo.setDimensions(new ArrayList<>(data.get(0).keySet()));
        histogramTemplateVo.setSource(data);
        return histogramTemplateVo;
    }

    private Object convertMap(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return data;
    }

    private Object convertBarChart(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return this.convertBarLinePieChart(parseDto, data);
    }

    private Object convertLineChart(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return this.convertBarLinePieChart(parseDto, data);
    }

    private Object convertPieChart(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return this.convertBarLinePieChart(parseDto, data);
    }

    private Object convertBarLinePieChart(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        List<String> dimensionConvertList = parseDto.getIndex().stream()
                .map(ParseColumnDto::getColumnComment)
                .collect(Collectors.toList());
        HistogramTemplateVo histogramTemplateVo = new HistogramTemplateVo();
        histogramTemplateVo.setDimensions(dimensionConvertList);
        histogramTemplateVo.setSource(data);
        return histogramTemplateVo;
    }

    private Object convertTableList(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return data;
    }

    private Object convertScatter(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return data;
    }
}
